/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package io.iec.edp.caf;

import io.iec.edp.caf.autoconfigure.EnvironmentPreparedConfiguration;
import io.iec.edp.caf.autoconfigure.PackageScanConfiguration;
import io.iec.edp.caf.commons.event.StartupCompletedEvent;
import io.iec.edp.caf.commons.event.StartupCompletedTask;
import io.iec.edp.caf.commons.runtime.CafEnvironment;
import io.iec.edp.caf.event.StartupEventPublisher;
import io.iec.edp.caf.msu.common.manager.AppManager;
import io.iec.edp.caf.multicontext.classloader.PlatformClassloader;
import io.iec.edp.caf.multicontext.context.PlatformApplicationContext;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import lombok.var;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.jpa.JpaRepositoriesAutoConfiguration;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Import;
import org.springframework.core.Ordered;
import org.springframework.lang.NonNull;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

/**
 * This is {@link CAFBootstrap}.
 *
 * @author yisiqi
 * @since 1.0.0
 */
@SpringBootApplication(
        scanBasePackages = {},
		exclude = {JpaRepositoriesAutoConfiguration.class}
)
@Import(PackageScanConfiguration.class)
@Slf4j
public class CAFBootstrap {
	private static Boolean parallel = false;
    @SneakyThrows
	public static void main(String[] args) {
		parallel = System.getProperty("parallel.startup", "false").equals("true");
		EnvironmentPreparedConfiguration.environmentPrepared();

		ConfigurableApplicationContext context;
		ConfigurableListableBeanFactory beanFactory;
        SpringApplication springApplication = new SpringApplication(CAFBootstrap.class);
		if (parallel) {
			//并行启动
			Thread.currentThread().setContextClassLoader(new PlatformClassloader(Thread.currentThread().getContextClassLoader()));
			springApplication.setApplicationContextClass(PlatformApplicationContext.class);
		} else {
			//非并行启动
			AppManager.enableAppManager(Thread.currentThread());
		}
		//启动程序

        context = springApplication.run(args);

		beanFactory = context.getBeanFactory();

		//输出启动日志
		StartupInfoLogger.logStarted(beanFactory);

		//设置临时目录
		setTempDir();

		//触发事件
        StartupEventPublisher eventPublisher = beanFactory.getBean(StartupEventPublisher.class);
        eventPublisher.onStartupCompleted(new StartupCompletedEvent(new StartupCompletedTask()));
        //todo 兼容老的启动后事件
        eventPublisher.onStartupCompleted(new io.iec.edp.caf.boot.event.startup.StartupCompletedEvent(new StartupCompletedTask()));
    }

	@Bean
	@ConditionalOnProperty("caf-boot.server.allow-bean-initialization-time-tracking")
	public BeanPostProcessor beanInitLogProcessor() {
		return new BeanInitializationTimingProcessor();
	}

	private static void setTempDir(){
		try{
			var caftemp = System.getenv("CAF_TMPDIR");
			var temp = "";

			if(caftemp==null){//证明用的老脚本，没有设置这个变量，将临时目录指向server/temp
//			log.error("caftemp null");
				var root = CafEnvironment.getServerRTPath();
				File currentDir = new File(root);
				String parentDirPath = currentDir.getParent();
				temp = parentDirPath+"/temp";
				System.setProperty("java.io.tmpdir",temp);
			}
		}catch (Exception e){
			log.error(e.getMessage(),e);
		}

//		log.error("caftemp:"+caftemp);
//		log.error("java.io.tmpdir:"+System.getProperty("java.io.tmpdir"));
		if(log.isInfoEnabled()){
			log.info("java.io.tmpdir:"+System.getProperty("java.io.tmpdir"));
		}
	}
	@Slf4j
	static class BeanInitializationTimingProcessor implements BeanPostProcessor, Ordered {

		private Map<String, Long> start;
		private Map<String, Long> end;

		public BeanInitializationTimingProcessor() {
			start = new HashMap<>();
			end = new HashMap<>();
		}

		@Override
		public Object postProcessBeforeInitialization(@NonNull Object bean, String beanName) throws BeansException {
			start.put(beanName, System.currentTimeMillis());
			return bean;
		}

		@Override
		public Object postProcessAfterInitialization(@NonNull Object bean, String beanName) throws BeansException {
			end.put(beanName, System.currentTimeMillis());
			long time = initializationTime(beanName);
			if (time < 5) {
				log.debug("It took {}ms to initialize \"{}\" bean(@{}).", time, beanName, bean.getClass().getCanonicalName());
			} else if (time < 10) {
				log.info("It took {}ms to initialize \"{}\" bean(@{}).", time, beanName, bean.getClass().getCanonicalName());
			} else {
				log.warn("It took {}ms to initialize \"{}\" bean(@{}).", time, beanName, bean.getClass().getCanonicalName());
			}
			return bean;
		}

		@Override
		public int getOrder() {
			return Integer.MAX_VALUE;
		}

		// this method returns initialization time of the bean.
		public long initializationTime(String beanName) {
			return end.get(beanName) - start.get(beanName);
		}
	}
}




